package net.protocol.diameter.avp;

import java.nio.ByteBuffer;

/**
 * <pre>
 *    DiameterURI
 * 
 *       The DiameterURI MUST follow the Uniform Resource Identifiers (URI)
 *       syntax [URI] rules specified below:
 * 
 *       "aaa://" FQDN [ port ] [ transport ] [ protocol ]
 * 
 *       The following are examples of valid Diameter host identities:
 * 
 *       aaa://host.example.com;transport=tcp
 *       aaa://host.example.com:6666;transport=tcp
 *       aaa://host.example.com;protocol=diameter
 *       aaa://host.example.com:6666;protocol=diameter
 *       aaa://host.example.com:6666;transport=tcp;protocol=diameter
 *       aaa://host.example.com:1813;transport=udp;protocol=radius
 * </pre>
 * 
 * @author jinhongw@gmail.com
 * @see <a href="http://tools.ietf.org/html/rfc3588#page-49">Diameter Base Protocol - DiameterURI</a>
 */
public class DiameterURI extends OctetString {

    /**
     * @param code AVP Code
     * @param uri AVP Data
     */
    public DiameterURI(int code, String uri) {
    	super(code, uri);
    }
    
	/**
	 * @param code AVP Code
	 * @param vendorId Vendor-ID
	 * @param uri AVP Data
	 */
	public DiameterURI(int code, int vendorId, String uri) {
		super(code, vendorId, uri);
	}
	
	/**
	 * @param code AVP Code
	 * @param uri AVP Data
	 */
	public DiameterURI(int code, DiameterURI.V uri) {
		this(code, DEFAULT_VENDOR_ID, uri);
	}

	/**
	 * @param code AVP Code
	 * @param vendorId Vendor-ID
	 * @param uri AVP Data
	 */
	public DiameterURI(int code, int vendorId, DiameterURI.V uri) {
		super(code, vendorId, uri.encode());
	}

    /**
     * Creates a new <code>DiameterURI</code> with the given DiameterAVPHeader and source ByteBuffer
     *
     * @param header Diameter AVP Header
     * @param src The buffer from which bytes are to be retrieved
     */
    DiameterURI(DiameterAVPHeader header, ByteBuffer src) {
        super(header, src);
    }
	
	@Override
	public DataFormat getType() {
		return DataFormat.DiameterURI;
	}

	@Override
	public String getName() {
		return DataFormat.DiameterURI.name();
	}

	/**
	 * This class represents the Diameter DiameterURI value
	 * 
	 * <pre>
	 *   The following are examples of valid Diameter host identities:
	 *     aaa://host.example.com;transport=tcp
	 *     aaa://host.example.com:6666;transport=tcp
	 *     aaa://host.example.com;protocol=diameter
	 *     aaa://host.example.com:6666;protocol=diameter
	 *     aaa://host.example.com:6666;transport=tcp;protocol=diameter
	 *     aaa://host.example.com:1813;transport=udp;protocol=radius
	 * </pre>
	 */
	public static class V {
		public static final int DEFAULT_PORT = 3868;
		public static final Transport DEFAULT_TRANSPORT = Transport.sctp;
		public static final Protocol DEFAULT_PROTOCOL = Protocol.diameter;

		// Fully Qualified Host Name
		private final String host;
		private int port = DEFAULT_PORT;
		private Transport transport = DEFAULT_TRANSPORT;
		private Protocol protocol = DEFAULT_PROTOCOL;

		public V(String host) {
			this(host, DEFAULT_TRANSPORT);
		}

		public V(String host, Transport transport) {
			this(host, DEFAULT_PORT, transport);
		}

		public V(String host, int port, Transport transport) {
			this(host, port, transport, DEFAULT_PROTOCOL);
		}

		/**
		 * @param host Fully Qualified Host Name
		 * @param port  Diameter port
		 * @param transport transport-protocol
		 * @param protocol protocol
		 * 
		 * @throws IllegalArgumentException 
		 * 			if protocol field is set to diameter and transport-protocol is udp
		 */
		public V(String host, int port, Transport transport,
				Protocol protocol) {
			if (protocol == Protocol.diameter && transport == Transport.udp) {
				String s = "transport-protocol(udp) must not be used when the protocol field is set to diameter.";
				throw new IllegalArgumentException(s);
			}
			
			this.host = host;
			this.port = port;
			this.transport = transport;
			this.protocol = protocol;
		}
		
		/**
		 * Returns a {@code DiameterURI.V} instance representing the specified uri String
		 * 
		 * @param uri a uri value -> aaa://host.example.com:3868;transport=tcp;protocol=diameter
		 * 
		 * @return a {@code DiameterURI.V} instance representing uri.
		 */
		public static V of(final String uri) {
			if (uri == null || uri.equals("")) return null;
			String host = null;
			int port = DEFAULT_PORT;
			Transport transport = DEFAULT_TRANSPORT;
			Protocol protocol = DEFAULT_PROTOCOL;
			
			String s = uri.replace(" ", "");
			s = s.substring(s.indexOf(":") + 3);
			String[] array = s.split(";");
			
			for (String e : array) {
				if (e.contains("transport")) {
					String t = e.substring(e.indexOf("=") + 1);
					transport = Transport.valueOf(t);
				} else if (e.contains("protocol")) {
					String t = e.substring(e.indexOf("=") + 1);
					if (t.contains("tacacs")) t = Protocol.tacacs.name();
					protocol = Protocol.valueOf(t);
				} else {
					if (e.contains(":")) {
						host = e.substring(0, e.indexOf(":"));
						port = Integer.valueOf(e.substring(e.indexOf(":") + 1));
					} else {
						host = e;
					}
				}
			}
			return new V(host, port, transport, protocol);
		}
		
		public String getHost() {
			return host;
		}

		public int getPort() {
			return port;
		}

		public Transport getTransport() {
			return transport;
		}

		public Protocol getProtocol() {
			return protocol;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((host == null) ? 0 : host.hashCode());
			result = prime * result + port;
			result = prime * result
					+ ((protocol == null) ? 0 : protocol.hashCode());
			result = prime * result
					+ ((transport == null) ? 0 : transport.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			V other = (V) obj;
			if (host == null) {
				if (other.host != null)
					return false;
			} else if (!host.equals(other.host))
				return false;
			if (port != other.port)
				return false;
			if (protocol != other.protocol)
				return false;
			if (transport != other.transport)
				return false;
			return true;
		}

		/**
		 * <pre>
		 * The string returned is of the form:
		 *   aaa://hostname:port;transport=transport-protocol;protocol=aaa-protocol
		 *   
		 *   For example:
		 *     aaa://host.example.com:3868;transport=sctp;protocol=diameter
		 * </pre>
		 * 
		 * @return a string representation of this <code>DiameterURI.V</code>
		 */
		@Override
		public String toString() {
			return encode();
		}

		private String encode() {
			StringBuilder buf = new StringBuilder();
			buf.append("aaa://").append(host);
			buf.append(":").append(port);
			buf.append(";transport=").append(transport.getValue());
			buf.append(";protocol=").append(protocol.getValue());
			return buf.toString();
		}
		
		public static enum Transport {
			tcp("tcp"), sctp("sctp"), udp("udp");

			private final String value;

			private Transport(String value) {
				this.value = value;
			}

			public String getValue() {
				return value;
			}
		}

		public static enum Protocol {
			diameter("diameter"), radius("radius"), tacacs("tacacs+");

			private final String value;

			private Protocol(String value) {
				this.value = value;
			}

			public String getValue() {
				return value;
			}
		}
	}
}
